package scene

import (
	"errors"
	"cgs-server/helper"
	"cgs-server/server"
	"cgs-server/server/assets/cgs"
	"cgs-server/server/base"
	"cgs-server/server/model"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"time"
)

/**
场景 Repository
 */
type SceneRepository interface {
	Add(model *AddModel) (*mongo.InsertOneResult, error)
	FindScene(id primitive.ObjectID, doc *bson.M) (find bool, err error)
	AddSceneData(collectionName string, data bson.A) (*mongo.InsertManyResult, error)
	FindByProjectId(id primitive.ObjectID) (*cgs.CgsProj, error)
	FindBySceneId(id primitive.ObjectID) (*Model, error)
	FindAllFromScene(sceneTable string) ([]interface{}, error)
	// 添加场景数据
	InsertSceneTable(sceneTableName string, sceneTable *[]interface{}) error
	// 删除场景数据
	DeleteSceneData(sceneDataName string) error

	// 查询场景数据
	FindSceneData(sceneDataName string, version int) ([]bson.M, error)
	base.Repository

	// 删除场景数据的表
	DropSceneDataCollection(sceneDataName string) error
}

func NewSceneRepository() SceneRepository {
	return &sceneRepository{
		base.RepositoryStruct{
			CollectionName: server.SceneCollectionName,
		},
	}
}

type sceneRepository struct {
	base.RepositoryStruct
}

func (s *sceneRepository) DeleteSceneData(sceneDataName string) error {
	if err := s.InitDB(); err != nil {
		return err
	}
	if sceneDataName == server.SceneCollectionName {
		return errors.New("collectionName is wrong")
	}

	_, err := s.Mongo.DeleteAll(sceneDataName)
	return err
}

func (s *sceneRepository) FindSceneData(sceneDataName string, version int) ([]bson.M, error) {
	err := s.InitDB()
	if err != nil {
		return nil, err
	}
	old := []bson.M{}

	if version == -1 {
		err = s.Mongo.FindAll(sceneDataName, &old)
	} else {
		filter := bson.M{
			server.VersionField: version,
		}
		err = s.Mongo.FindMany(sceneDataName+server.HistorySuffix, filter, &old)
	}

	if err != nil {
		return nil, err
	}
	return old, nil
}

func (s *sceneRepository) Add(model *AddModel) (*mongo.InsertOneResult, error) {
	if s.Mongo == nil {
		return nil, errors.New("db is not created")
	}
	pinyin := helper.ConvertToPinYin(model.Name)
	now := time.Now()
	doc := bson.M{
		"ID":             model.IDObjId,
		"Name":           model.Name,
		"TotalPinYin":    pinyin.TotalPinYin,
		"FirstPinYin":    pinyin.FirstPinYin,
		"CollectionName": model.CollectionName,
		"Version":        model.Version,
		"CreateTime":     now,
		"UpdateTime":     now,
		"IsPublic":       false,
		"UserID": 		  model.UserId,
	}
	return s.Mongo.InsertOne(server.SceneCollectionName, doc)
}

func (s *sceneRepository) AddSceneData(collectionName string, data bson.A) (*mongo.InsertManyResult, error) {
	if s.Mongo == nil {
		return nil, errors.New("db is not created")
	}
	if collectionName == "" || data == nil {
		return nil, errors.New("collectionName or data can not be empty")
	}
	// 这里需要判断集合名称，防止删错
	if collectionName == server.SceneCollectionName {
		return nil, errors.New("collectionName is wrong")
	}

	_, err := s.Mongo.DeleteAll(collectionName)
	if err != nil {
		return nil, err
	}
	return s.Mongo.InsertMany(collectionName, data)
}

func (s *sceneRepository) FindScene(id primitive.ObjectID, doc *bson.M) (find bool, err error) {
	if s.Mongo == nil {
		return false, errors.New("db is not created")
	}

	filter := bson.M{
		"ID": id,
	}
	// server.SceneCollectionName
	return s.Mongo.FindOne(s.CollectionName, filter, doc)
}

func (s *sceneRepository) FindByProjectId(id primitive.ObjectID) (*cgs.CgsProj, error) {
	db, err := server.Mongo()
	if err != nil {
		return nil, err
	}
	filter := bson.M{
		"ID": id,
	}
	var result cgs.CgsProj
	_, err = db.FindOne(server.CgsProjectCollectionName, filter, &result)
	return &result, err
}

func (s *sceneRepository) FindBySceneId(id primitive.ObjectID) (*Model, error) {
	db, err := server.Mongo()
	if err != nil {
		return nil, err
	}
	filter := bson.M{
		"ID": id,
	}
	var result Model
	_, err = db.FindOne(server.SceneCollectionName, filter, &result)
	return &result, err
}

func (s *sceneRepository) FindAllFromScene(sceneTable string) ([]interface{}, error) {
	//获得scene表格数据
	db, err := server.Mongo()
	if err != nil {
		return nil, err
	}
	var result []interface{}
	err = db.FindAll(sceneTable, &result)
	if err != nil {
		return nil, err
	}
	return result, err
}

func (s *sceneRepository) InsertSceneTable(sceneTableName string, sceneTable *[]interface{}) error {
	//获得scene表格数据
	db, err := server.Mongo()
	if err != nil {
		return  err
	}
	if sceneTable == nil || len(*sceneTable) == 0 {
		return errors.New("scene data is nil or empty")
	}
	_, err = db.InsertMany(sceneTableName, *sceneTable)
	return err
}

func (s *sceneRepository) FindOne(mdl *model.DataUserModel) (interface{}, error) {
	if scene, ok := mdl.Data.(*Model); ok {
		if err := s.CheckDB(); err != nil {
			return nil, err
		}
		filter := bson.M{
			"ID": scene.ID,
		}
		scene.ResolveFilter(scene, mdl.User, &filter)
		result := &Model{}
		find, err := s.Mongo.FindOne(s.CollectionName, filter, result)
		if find {
			return result, nil
		}
		return nil, err
	} else {
		return nil, errors.New("Scene type error")
	}
}

func (s *sceneRepository) Insert(mdl *model.DataUserModel) error {
	if scene, ok := mdl.Data.(*Model); ok {
		if err := s.CheckDB(); err != nil {
			return err
		}
		scene.ResolveEntity(scene, mdl.User)
		_, err := s.Mongo.InsertOne(s.CollectionName, scene)
		return err
	} else {
		return errors.New("Scene type error")
	}
}

func (s *sceneRepository) Update(mdl *model.DataUserModel) error {
	if scene, ok := mdl.Data.(*Model); ok {
		if err := s.CheckDB(); err != nil {
			return err
		}
		fiter := bson.M{
			"ID": scene.ID,
		}
		scene.ResolveFilter(scene, mdl.User, &fiter)
		set := bson.M{
			"UpdateTime": time.Now(),
		}
		if scene.Name != "" {
			set["Name"] = scene.Name
		}
		if scene.TotalPinYin != "" {
			set["TotalPinYin"] = scene.TotalPinYin
		}
		if scene.FirstPinYin != "" {
			set["FirstPinYin"] = scene.FirstPinYin
		}
		if scene.Version != nil {
			set["Version"] = scene.Version
		}
		if scene.IsPublic != nil {
			set["IsPublic"] = scene.IsPublic
		}
		if scene.Thumbnail != "" {
			set["Thumbnail"] = scene.Thumbnail
		}
		scene.ResolveUpdateCondition(scene, mdl.User, &set)
		update := bson.M{
			"$set": set,
		}
		if scene.CategoryID == "" {
			update["$unset"] = bson.M{
				"Category": 1,
			}
		} else {
			set["Category"] = scene.CategoryID
		}
		_, err := s.Mongo.UpdateOne(s.CollectionName, fiter, update)
		return err
	} else {
		return errors.New("Scene type error")
	}
}

func (s *sceneRepository) Find(mdl *model.DataUserModel) (interface{}, error) {
	if scene, ok := mdl.Data.(*Model); ok {
		if err := s.CheckDB(); err != nil {
			return nil, err
		}
		filter := bson.M{}
		if mdl.Filter != nil {
			filter = *mdl.Filter
		}
		scene.ResolveFilter(scene, mdl.User, &filter)
		opts := options.FindOptions{}
		if mdl.Options != nil {
			opts = *mdl.Options
		}
		results := []*Model{}
		err := s.Mongo.FindMany(s.CollectionName, filter, &results, &opts)
		if err != nil {
			return nil, err
		}
		return results, nil
	} else {
		return nil, model.NewTypeError("scene")
	}
}

func(s *sceneRepository) Delete(mdl *model.DataUserModel) error {
	if scene, ok := mdl.Data.(*Model); ok {
		if err := s.CheckDB(); err != nil {
			return err
		}
		filter := bson.M{
			"ID": scene.ID,
		}
		_, err := s.Mongo.DeleteOne(s.CollectionName, filter)
		return err
	} else {
		return model.NewTypeError("scene")
	}
}

func(s *sceneRepository) DeleteLogic(mdl *model.DataUserModel) error {
	if scene, ok := mdl.Data.(*Model); ok {
		if err := s.CheckDB(); err != nil {
			return err
		}
		filter := bson.M{
			"ID": scene.ID,
		}
		set := bson.M{}
		scene.DeleteLogic(scene, mdl.User, &set)
		update := bson.M{
			"$set": set,
		}

		_, err := s.Mongo.UpdateOne(s.CollectionName, filter, update)
		return err
	} else {
		return model.NewTypeError("scene")
	}
}

func (s *sceneRepository) DropSceneDataCollection(sceneDataName string) error {
	if err := s.InitDB(); err != nil {
		return err
	}
	if sceneDataName == server.SceneCollectionName {
		return errors.New("collectionName is wrong")
	}
	err := s.Mongo.DropCollection(sceneDataName)
	return err
}
